package org.moparscape.gamedev.brickbreaker.game;

import java.io.FileReader;
import java.io.IOException;
import java.util.HashMap;
import java.util.Properties;
import org.newdawn.slick.AppGameContainer;
import org.newdawn.slick.GameContainer;
import org.newdawn.slick.Graphics;
import org.newdawn.slick.BasicGame;
import org.newdawn.slick.Image;
import org.newdawn.slick.Input;
import org.newdawn.slick.SlickException;
import org.newdawn.slick.geom.Circle;
import org.newdawn.slick.geom.Line;
import org.newdawn.slick.geom.Rectangle;
import org.newdawn.slick.geom.Shape;

public class BrickBreaker extends BasicGame {
	
	private enum Direction { 
		NORTH, SOUTH, EAST, WEST 
	}
	
	private enum Status {
		WAITING_TO_LAUNCH, NORMAL
	}

	private int paddleSpeed = 4;
	private int ballDirX = 0, ballDirY = -1;
	private float ballSpeedX = 0, ballSpeedY = 3;
	private int remainingBalls = 2;
	private Brick[][] board = new Brick[10][5];
	HashMap<String, Image> sprites = new HashMap<String, Image>();
	private Rectangle paddle;
	private Circle ball;
	private Status status = Status.WAITING_TO_LAUNCH;

	public static void main(String[] args) {
		try {
			new AppGameContainer(new BrickBreaker(), 450, 600, false).start();
		} catch (SlickException e) {
			e.printStackTrace();
		}
	}
	
	public BrickBreaker() {
		super("Brick Breaker - by David Insley");
	}
	
	@Override
	public void render(GameContainer arg0, Graphics arg1) throws SlickException {
		getSprite("background").draw();
		for(int i = 0; i < remainingBalls; i++) {
			getSprite("ball").draw((ball.getWidth() + 5) * i + 5, arg0.getHeight() - ball.getHeight() - 5);
		}
		for(int x = 0; x < board.length; x++) {
			for(int y = 0; y < board[x].length; y++) {
				Brick brick = board[x][y];
				if(brick == null) {
					continue;
				}
				Image sprite = getSprite("brick" + brick.getBrickID());
				sprite.draw(brick.getX(), brick.getY());
			}
		}
		getSprite("paddle").draw((float)paddle.getX(), (float)paddle.getY());
		getSprite("ball").draw(ball.getX(), ball.getY());
	}

	@Override
	public void init(GameContainer arg0) throws SlickException {
		try {
			Properties properties = new Properties();
			properties.load(new FileReader("props.ini"));
			String resourceDir = properties.getProperty("resource_dir");
			loadSprite("ball", resourceDir + properties.getProperty("ball_sprite"));
			loadSprite("paddle", resourceDir + properties.getProperty("paddle_sprite"));
			loadSprite("background", resourceDir + properties.getProperty("background"));
			int brickCount = Integer.parseInt(properties.getProperty("brick_count"));
			String brickName = properties.getProperty("brick_name");
			for(int i = 0; i < brickCount; i++) {
				loadSprite("brick" + i, resourceDir + brickName.replace("#", Integer.toString(i)));
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
		paddle = new Rectangle((arg0.getWidth()/2) - (getSprite("paddle").getWidth()/2), arg0.getHeight() -getSprite("paddle").getHeight() - 10, getSprite("paddle").getWidth(), getSprite("paddle").getHeight());
		ball = new Circle(paddle.getCenterX(), paddle.getCenterY()-(paddle.getHeight()/2)-(getSprite("ball").getWidth()/2), (getSprite("ball").getWidth()/2));
		for(int x = 0; x < board.length; x++) {
			for(int y = 0; y < board[x].length; y++) {
				board[x][y] = new Brick((int)(Math.random()*7), x * getSprite("brick0").getWidth(), y * getSprite("brick0").getHeight(), getSprite("brick0").getWidth(), getSprite("brick0").getHeight());
			}
		}
	}

	@Override
	public void update(GameContainer arg0, int arg1) throws SlickException {
		Input input = arg0.getInput();
		int dir = 0;
		if(input.isKeyDown(Input.KEY_LEFT)) {
			dir--;
		}
		if(input.isKeyDown(Input.KEY_RIGHT)) {
			dir++;
		}
		int x = (int) (paddle.getX() + dir*paddleSpeed);
		if(x >= 0 && x + paddle.getWidth() <= arg0.getWidth()) {
			paddle.setX(x);
		}
		switch(status) {
			case WAITING_TO_LAUNCH:
				if(ball.getCenterX() != paddle.getCenterX()) {
					ball.setCenterX(paddle.getCenterX());
				}
				if(input.isKeyPressed(Input.KEY_SPACE)) {
					status = Status.NORMAL;
				}
				break;
			case NORMAL:
				ball.setX(ball.getX() + ballDirX * ballSpeedX);
				ball.setY(ball.getY() + ballDirY * ballSpeedY);
				Direction d = checkBallCollision();
				if(d != null) {
					switch(d) {
						case NORTH:
						case SOUTH:
							ballDirY *= -1;
							return;
						case EAST:
						case WEST:
							ballDirX *= -1;
							return;
					}
				}
				if((d = getCollisionDirection(ball, paddle)) != null) {
					ballSpeedX += .2;
					ballSpeedY += .2;
					switch(d) {
						case NORTH:
							ballDirY *= -1;
							ballDirX = (int) ((((ball.getX() - paddle.getX())/(paddle.getWidth() - ball.getWidth())) - 0.5) * 5);
							return;
						case EAST:
						case WEST:
							ballDirX *= -1;
							return;
					}
				}
				if(ball.getX() <= 0 || ball.getX() >= arg0.getWidth() - ball.getWidth()) {
					ballDirX *= -1;
				} else if(ball.getY() <= 0) {
					ballDirY *= -1;
				} else if(ball.getY() >= arg0.getHeight()) {
					ball.setCenterY(paddle.getY() - ball.getRadius());
					ball.setCenterX(paddle.getCenterX());
					ballDirX = 0;
					ballSpeedX = 0;
					ballDirY = -1;
					ballSpeedY = 3;
					remainingBalls--;
					status = Status.WAITING_TO_LAUNCH;
				}
				break;
		}
	}

	private Direction checkBallCollision() {
		for(int x = 0; x < board.length; x++) {
			for(int y = 0; y < board[x].length; y++) {
				Brick brick = board[x][y];
				if(brick == null) {
					continue;
				}
				Direction dir = getCollisionDirection(ball, brick);
				if(dir != null) {
					board[x][y] = null;
					return dir;
				}
			}
		}
		return null;
	}

	private void loadSprite(String name, String resource) throws SlickException {
		sprites.put(name, new Image(resource));
	}

	private Image getSprite(String name) {
		return sprites.get(name);
	}

	private Direction getCollisionDirection(Shape shape, Shape source) {
		if(shape.intersects(new Line(source.getX(), source.getY(), source.getX() + source.getWidth(), source.getY()))) {
			return Direction.NORTH;
		}
		if(shape.intersects(new Line(source.getX(), source.getY() + source.getHeight(), source.getX() + source.getWidth(), source.getY() + source.getHeight()))) {
			return Direction.SOUTH;
		}
		if(shape.intersects(new Line(source.getX() + source.getWidth(), source.getY(), source.getX() + source.getWidth(), source.getY() + source.getHeight()))) {
			return Direction.EAST;
		}
		if(shape.intersects(new Line(source.getX(), source.getY(), source.getX(), source.getY() + source.getHeight()))) {
			return Direction.WEST;
		}
		return null;
	}
	
}
